﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace SocketTestClient.Sender
{
    public class SocketSender : ISender
    {
        private Socket _socket = null;
        private byte[] _rvcBuffer;
        private int _rcvBufferLength = 1024 * 1024;
        private DeamonState _state;
        private string _ipAddress = "127.0.0.1";
        private int _port = 9000;
        private PacketAnalyzer _packetAnalyzer;
        private MessageQueue _queue;
        private Thread _mainThread;

        public SocketSender()
        {
            _rvcBuffer = new byte[_rcvBufferLength];
            _packetAnalyzer = new PacketAnalyzer();
            _queue = new MessageQueue();
            _mainThread = new Thread(new ThreadStart(WorkMain));


            // For UDP
            // Socket _socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); 

            Connect();
            _mainThread.Start();
        }

        public int Send(IRequest req)
        {
            if (State == DeamonState.Disconnected)
                return -1;

            RequireCell cell = new RequireCell();
            cell.Request = req;
            _queue.Send(cell);

            if (cell.WaitDone(1000 * 60 * 3) == false)
                return -1;
            else
                return 0;
        }


        public DeamonState State
        {
            get { return _state; }
        }

        private bool Connect()
        {
            bool isConnect = false;
            if (State == DeamonState.Connected)
                return true;

            try
            {
                _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                _socket.Connect(_ipAddress, _port);
                isConnect = true;
                Print("Connect to " + _ipAddress + ":" + _port);
                _state = DeamonState.Connected;
            }
            catch (Exception e)
            {
                isConnect = false;
                Disconnect();

            }
            return isConnect;
        }

        private void Disconnect()
        {
            if (_state == DeamonState.Disconnected)
                return;
            try
            {
                _socket.Close();
            }
            catch(Exception e)
            {

            }
            _socket = null;
            Print("Disconnected.");
            _state = DeamonState.Disconnected;
        }

        private void WorkMain()
        {
            while (true)
            {

                if (State == DeamonState.Disconnected)
                {
                    Connect();
                }
                if (State == DeamonState.Disconnected)
                {
                    Print("wait connection.");

                    Thread.Sleep(100);
                    continue;
                }

                if (_queue.Peek(100) == null)
                {
                    Print("wait packet.");

                    continue;
                }

                RequireCell reqCell = _queue.Receive() as RequireCell;
                IRequest req = reqCell.Request;
                int rcvCount = 0;
                byte[] rcvBuf = null;
                byte[] sndBuf = req.GetBytes();

                Print("packet sending started: " + req.GetType());


                _packetAnalyzer.SetBufferLength(sndBuf.Length);
                try
                {
                    _socket.Send(_packetAnalyzer.GetHeader());
                    _socket.Send(sndBuf);

                    while (true)
                    {

                        rcvCount = _socket.Receive(_rvcBuffer);
                        if (rcvCount <= 0)
                        {
                            Print("rcv error.");
                            Disconnect();
                            break;
                        }
                        rcvBuf = _packetAnalyzer.SetBuffer(_rvcBuffer, rcvCount);
                        Print("rcv: " + rcvCount + ", Total:" + _packetAnalyzer.TotalLength + ", remain:" + _packetAnalyzer.DataRemain);
                        if (rcvBuf != null)
                            break;
                    }
                    req.FromBytes(rcvBuf, rcvBuf.Length);

                }
                catch (Exception e)
                {
                    Print("socket exception occured.");

                    Disconnect();
                    reqCell.Done();
                    continue;
                }

                reqCell.Done();
                Print("packet sending end.");

            }
        }
        private void Print(string str)
        {
            System.Console.WriteLine("[SockSnd]"+str);
        }
    }
}
